﻿using System;
using System.Threading;
using System.Collections.Generic;

namespace WFIM.Framework.Parellel
{
  /// <include file='XMLCodeDocumentation.xml' path='documentation/item[@name="T_ParallelTaskPool"]/*' />
  public class ParallelTaskPool : ParallelTaskManager
  {
    //-----------------------------------------------------------------------
    // FIELDS
    //-----------------------------------------------------------------------
    private static  Semaphore           poolLimiter       = null;
    private static  EventWaitHandle     emptyQueueBlocker = null;
    private static  Thread              queueRunner       = null;
    private static  Queue<ParallelTask> queue             = null;
    
    //-----------------------------------------------------------------------
    // CONSTRUCTORS
    //-----------------------------------------------------------------------
    static ParallelTaskPool()
    {
      //create a waithandle
      emptyQueueBlocker   = new EventWaitHandle(false, EventResetMode.ManualReset);

      //get number of concurrent threads in pool
      int max, dummy;
      ThreadPool.GetMaxThreads(out max, out dummy);

      //double the max thread count, so tasks can be queued in the threadpool before execution
      max *= 2;

      //create pool limiter, to prevent floading the threadpool
      poolLimiter = new Semaphore(max, max);

      //create a local task queue
      queue = new Queue<ParallelTask>(max);
      
      //create a queue handler thread
      queueRunner = new Thread(new ThreadStart(queueHandler));
      queueRunner.IsBackground = true;
      queueRunner.Start();
    }

    //-----------------------------------------------------------------------
    protected ParallelTaskPool()
    {
    }

    //-----------------------------------------------------------------------
    // METHODS
    //-----------------------------------------------------------------------
    private static void queueHandler()
    {
      //wait for a new entry in the queue
      while (emptyQueueBlocker.WaitOne())
      {
        //dequeue a task from the local queue
        ParallelTask task;
        lock (queue)
          task = queue.Dequeue();

        //wait for free space in the threadpool
        poolLimiter.WaitOne();

        //run the task in the threadpool (or queue if pool has no free threads)
        if (!task.run())
          throw new TaskFailedToRunException(task);

        lock (queue)
        {
          //check for empty queue
          if (queue.Count == 0)
          {
            //set blocker to wait
            emptyQueueBlocker.Reset();

            //set queue-runner thread to background
            queueRunner.IsBackground = true;
          }
        }
      }
    }

    //-----------------------------------------------------------------------
    /// <summary>Runs a task responsibly on the threadpool.</summary>
    /// <remarks>
    /// If there are too many tasks to run responsibly at once on the threadpool, tasks are temporary queued in memory until free threadpool space is available.
    /// </remarks>
    /// <param name="_task">The task to run.</param>
    public static void runTask(ParallelTask _task)
    {
      //set automatic callback for the endTask call
      _task.setParentCallback(new ParallelTask.TaskParentCallback(endTask));

      //set queue-runner thread to foreground
      queueRunner.IsBackground = false;

      lock (queue)
      {
        //enqueue the task in the local queue
        queue.Enqueue(_task);

        //signal that the local queue is not empty
        emptyQueueBlocker.Set();
      }

      //count the number of concurring tasks (although it may still be queued)
      incrementTaskCounter();
    }
    
    //-----------------------------------------------------------------------
    private static void endTask(ParallelState _state)
    {
      //enable free space in threadpool
      poolLimiter.Release();

      //count the number of concurring tasks
      decrementTaskCounter();
    }
    
    //-----------------------------------------------------------------------
  }
}
